/* Endless Code Copyright (C) 2009 Jingul,Kim
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
package ec.gfx;

import ec.Result;
import ec.util.Matrix;
import ec.util.Point;

/**
 * @since 2009
 * @version 1.0
 * @author <a href="mailto:jingul.kim@gmail.com">Jingul,Kim</a>
 */
public class Scene extends Node {
	private long m_elpased;

	private Matrix m_toWorld = new Matrix();

	private int[] viewport = new int[4];

	private Root root;

	public Scene() {
		root = new Root();
	}

	public void update(long elapsed) {
		m_elpased = elapsed;
		
		root.update(this);
	}

	private void inverseWorld() {
		float[] proj = new float[16];
		float[] mv = new float[16];
		
		GL.enable(GL.GL_TEXTURE_2D);

		GL.getFloatv(GL.GL_PROJECTION_MATRIX, proj);
		GL.getFloatv(GL.GL_MODELVIEW_MATRIX, mv);

		Matrix.inverse(m_toWorld, Matrix.multi(proj, mv));
	}

	public void render() {
		if (pre(this) == Result.OK) {
			render(this);
		}

		post(this);
	}

	public int picking(int x, int y) {
		int hits = 0;
		
		GL.matrixMode(GL.GL_PROJECTION);
		GL.pushMatrix();

		GL.renderMode(GL.GL_SELECT);
		GL.loadIdentity();
		
		GLU.pickMatrix(x, viewport[3] - y, 2, 2, viewport);
		ortho(viewport[2], viewport[3]);

		render();
		
		hits = GL.renderMode(GL.GL_RENDER);

		GL.matrixMode(GL.GL_PROJECTION);
		GL.popMatrix();

		return hits;
	}

	public long getElapsed() {
		return m_elpased;
	}

	public void realToWorld(Point p, int x, int y) {
		final float[] toWorld = m_toWorld.getArray();

		float xc, yc;

		xc = (float) (2.0f * x / viewport[2]) - 1.0f;
		yc = -((float) (2.0f * y / viewport[3]) - 1.0f);

		p.m_x = toWorld[0] * xc + toWorld[4] * yc + toWorld[12];
		p.m_y = toWorld[1] * xc + toWorld[5] * yc + toWorld[13];
	}

	@Override
	public Result pre(Scene scene) {
		GL.clear(GL.GL_COLOR_BUFFER_BIT  | GL.GL_STENCIL_BUFFER_BIT);

		GL.matrixMode(GL.GL_MODELVIEW);
		GL.loadIdentity();

		GL.initNames();
		GL.pushName(0);

		inverseWorld();

		return Result.OK;
	}

	@Override
	public Result render(Scene scene) {
		if (root.pre(this) == Result.OK) {
			root.now(this);
			root.render(this);
		}

		root.post(this);

		return Result.OK;
	}

	public void resize(int x, int y, int w, int h) {
		GL.viewport(x, y, w, h);
		GL.getIntegerv(GL.GL_VIEWPORT, viewport);
		
		GL.matrixMode(GL.GL_PROJECTION);
		GL.loadIdentity();

		ortho(w, h);

		GL.matrixMode(GL.GL_MODELVIEW);
		GL.loadIdentity();
	}

	private void ortho(int w, int h) {
//		final float degree = 50.0f;
//
//		if (w <= h)
//			GL.ortho(-degree, degree, -degree * (float) h / (float) w, degree
//					* (float) h / (float) w, -1.0, 1.0);
//		else
//			GL.ortho(-degree * (float) w / (float) h, degree * (float) w
//					/ (float) h, -degree, degree, -1.0, 1.0);
		GLU.ortho2D(viewport[0], viewport[2], viewport[1], viewport[3]);
	}
}
